package com.yanqu.road.server.manger.activity.ppl;

import com.alibaba.fastjson.JSON;
import com.yanqu.road.entity.activity.ActivityInfo;
import com.yanqu.road.entity.activity.ppl.BallEliminate;
import com.yanqu.road.entity.activity.ppl.PplDrumInfo;
import com.yanqu.road.entity.activity.ppl.PplGoods;
import com.yanqu.road.entity.activity.ppl.PplLine;
import com.yanqu.road.entity.activity.ppl.PplShootResult;
import com.yanqu.road.entity.activity.ppl.PplUserData;
import com.yanqu.road.entity.config.goods.GoodsInfo;
import com.yanqu.road.entity.config.goods.OpenGoodsBagResult;
import com.yanqu.road.entity.enums.eActivityType;
import com.yanqu.road.entity.enums.eSystemId;
import com.yanqu.road.server.TempMgr;
import com.yanqu.road.server.gameplayer.GamePlayer;
import com.yanqu.road.server.gameplayer.module.activity.ppl.PplModule;
import com.yanqu.road.server.manger.activity.ActivityMgr;
import com.yanqu.road.server.manger.activity.NormalActivityMgr;
import com.yanqu.road.server.manger.config.GoodsBagMgr;
import com.yanqu.road.server.manger.config.SystemOpenMgr;
import com.yanqu.road.server.manger.player.GamePlayerMgr;
import com.yanqu.road.server.manger.system.CommonMgr;
import com.yanqu.road.server.protocol.GameErrorCode;
import com.yanqu.road.utils.property.PropertyHelper;
import com.yanqu.road.utils.string.StringUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 泡泡龙Mgr
 */
public class PplMgr extends TempMgr {

    private static ActivityInfo activityInfo;

    private static List<PplDrumInfo> drumInfoList = new ArrayList<>();

    private static Map<Integer, PplGoods> pplGoodsMap = new ConcurrentHashMap<>(); // 活动道具（体力药、琴谱）

    public static int PPL_SCORE_GOODS_ID = 101917; // 泡泡龙积分ID
    public static int PPL_BOX_BALL_BOX_ID = 121025; // 泡泡龙宝箱球的宝箱ID

    public static int PPL_ENERGY_RECOVERY_ID = 120123; // 泡泡龙体力恢复道具
    public static int PPL_BOOK_MOUNTAIN_RIVER_ID = 120126; // 泡泡龙琴谱高山流水
    public static int PPL_BOOK_FISHING_SONG_ID = 120125; //泡泡龙琴谱渔歌晚唱

    public static int MAX_ENERGY = 500; // 体力上限
    public static int ENERGY_RECOVERY_TIME_SECOND = 600; // 体力恢复时间（秒）
    public static int ENERGY_RECOVERY_COUNT = 10; // 体力恢复数量

    private static int SAME_COLOR_RATIO_A = 500; // 概率A
    private static int SAME_COLOR_RATIO_B = 300; // 概率B
    private static int SAME_COLOR_RATIO_C = 100; // 概率C
    private static int BOX_RATIO = 50; // 宝箱概率（先计算非同色，再算宝箱）

    public static int MULTI_UNLOCK_SCORE = 2000; // 多倍开启积分
    public static int FAST_UNLOCK_VIP_LEVEL = Integer.MAX_VALUE; // 急速倍开启VIP等级

    // 写死的数据
    private static final int MAX_BALLS_COUNT = 10; // 每行最大的数量
    private static final int LONG_BALLS_COUNT = 8; // 长行的球数量
    private static final int SHORT_BALLS_COUNT = 2; // 短行的球数量
    private static final int GAME_OVER_LENGTH = 13; // 游戏结束行数
    private static final int SHOW_LENGTH = 9; // 最小显示长度
    private static final int ELIMINATE_NEED_COUNT = 3; // 消除所需数量
    public static final int TOTAL_COLOR_COUNT = 4; // 总颜色数
    public static final int INIT_Y = 5; // 初始Y值

    @Override
    public boolean reloadConfig() throws Exception {
        return true;
    }

    @Override
    public boolean reloadData() throws Exception {
        return true;
    }

    @Override
    public boolean init() throws Exception {
        return reload();
    }

    @Override
    public boolean stop() {
        return false;
    }

    public static void reloadActivityData() {
        getLogger().info("reload PplMgr start");

        // 获取活动
        ActivityInfo tmpActivityInfo = null;
        List<ActivityInfo> openActivityInfoList = NormalActivityMgr.getOpenActivityInfoList(eActivityType.PplActivity.getValue());
        if (openActivityInfoList.size() <= 0) {
            return;
        }
        tmpActivityInfo = openActivityInfoList.get(0);

        // 校验参数
        if (!checkActivityParam(tmpActivityInfo)) {
            return;
        }

        // 概率
        List<String> ratioList = StringUtils.stringToStringList(tmpActivityInfo.getParam3(), "-");
        List<Integer> abcRatio = StringUtils.stringToIntegerList(ratioList.get(0), ";");
        BOX_RATIO = Integer.valueOf(ratioList.get(1));
        SAME_COLOR_RATIO_A = abcRatio.get(0);
        SAME_COLOR_RATIO_B = abcRatio.get(1);
        SAME_COLOR_RATIO_C = abcRatio.get(2);
        // 多倍开启所需积分
        if (ratioList.size() >= 3) {
            MULTI_UNLOCK_SCORE = Integer.valueOf(ratioList.get(2));
        } else {
            MULTI_UNLOCK_SCORE = 2000; // 没设置，就给默认值
        }
        // 急速倍开启积分
        if (ratioList.size() >= 4) {
            FAST_UNLOCK_VIP_LEVEL = Integer.valueOf(ratioList.get(3));
        } else {
            FAST_UNLOCK_VIP_LEVEL = Integer.MAX_VALUE; // 没设置，就给默认值
        }

        // 体力恢复
        List<Integer> energyList = StringUtils.stringToIntegerList(tmpActivityInfo.getParam4(), "\\|");
        MAX_ENERGY = energyList.get(0);
        ENERGY_RECOVERY_TIME_SECOND = energyList.get(1);
        ENERGY_RECOVERY_COUNT = energyList.get(2);

        // 鼓
        List<PplDrumInfo> tmpDrumInfoList = new ArrayList<>();
        List<String> drumList = StringUtils.stringToStringList(tmpActivityInfo.getParam5(), "\\|");
        for (int i = 0; i < drumList.size(); i++) {
            List<String> drum = StringUtils.stringToStringList(drumList.get(i), ";");
            PplDrumInfo drumInfo = new PplDrumInfo();
            drumInfo.setIndex(i);
            drumInfo.setWeight(Integer.valueOf(drum.get(0)));
            drumInfo.setNeedBallCount(Integer.valueOf(drum.get(1)));
            drumInfo.setOneBallScore(Integer.valueOf(drum.get(2)));
            drumInfo.setFullReward(drum.get(3));
            tmpDrumInfoList.add(drumInfo);
        }
        drumInfoList = tmpDrumInfoList;

        // 道具
        List<Integer> itemIdList = StringUtils.stringToIntegerList(tmpActivityInfo.getParam6(), "\\|");
        PPL_SCORE_GOODS_ID = itemIdList.get(0);
        PPL_ENERGY_RECOVERY_ID = itemIdList.get(1);
        PPL_BOOK_MOUNTAIN_RIVER_ID = itemIdList.get(2);
        PPL_BOOK_FISHING_SONG_ID = itemIdList.get(3);
        PPL_BOX_BALL_BOX_ID = itemIdList.get(4);
        initPplGoodsMap();

        activityInfo = tmpActivityInfo;

        reloadPlayerData();

        getLogger().info("reload PplMgr end");
    }

    public static ActivityInfo getActivityInfo() {
        return activityInfo;
    }

    private static boolean checkActivityParam(ActivityInfo tmpActivityInfo) {
        String checkName = "";
        try {
            // 概率
            checkName = "param3";
            List<String> ratioList = StringUtils.stringToStringList(tmpActivityInfo.getParam3(), "-");
            List<Integer> abcRatio = StringUtils.stringToIntegerList(ratioList.get(0), ";");
            Integer boxRatio = Integer.valueOf(ratioList.get(1));
            if (abcRatio.size() < 3) {
                throw new Exception("Ppl activity abc ratio size error!");
            }
            // 有配置多倍开启积分
            if (ratioList.size() >= 3) {
                Integer multiUnlockScore = Integer.valueOf(ratioList.get(2));
            }


            // 体力恢复
            checkName = "param4";
            List<Integer> energyList = StringUtils.stringToIntegerList(tmpActivityInfo.getParam4(), "\\|");
            if (energyList.size() < 3) {
                throw new Exception("Ppl activity param4 size error!");
            }

            // 鼓
            checkName = "param5";
            List<String> drumList = StringUtils.stringToStringList(tmpActivityInfo.getParam5(), "\\|");
            for (int i = 0; i < drumList.size(); i++) {
                List<String> drum = StringUtils.stringToStringList(drumList.get(i), ";");
                if (drum.size() < 4) {
                    throw new Exception("Ppl activity drum param size error!");
                }
                for (int j = 0; j < 3; j++) {
                    Integer value = Integer.valueOf(drum.get(j));
                }
            }

            // 道具
            checkName = "param6";
            List<Integer> itemIdList = StringUtils.stringToIntegerList(tmpActivityInfo.getParam6(), "\\|");
            if (itemIdList.size() < 5) {
                throw new Exception("Ppl activity item size error!");
            }
            for (int i = 0; i < itemIdList.size(); i++) {
                GoodsInfo goodsInfo = CommonMgr.getGoodsInfo(itemIdList.get(i));
                if (goodsInfo == null) {
                    throw new Exception("Ppl activity can not find goodsInfo!");
                }
            }
        } catch (Exception e) {
            getLogger().error("PplMgr : activity {} error!!!", checkName);
            getLogger().error(e);
            return false;
        }
        return true;
    }

    /**
     * 获取展示中的活动ID（0表示没活动）
     */
    public static int getShowTimeActivityId() {
        return activityInfo == null ? 0 : activityInfo.getActivityId();
    }

    /**
     * 是否在展示时间内
     */
    public static boolean isActivityInShowTime() {
        if (activityInfo == null) {
            return false;
        }
        return ActivityMgr.activityInShowTime(activityInfo);
    }

    /**
     * 是否在操作时间内
     */
    public static boolean isActivityInTime() {
        if (activityInfo == null) {
            return false;
        }
        return ActivityMgr.activityInTime(activityInfo);
    }

    /**
     * 是否在选择美女时间内（活动期+领奖期）
     */
    public static boolean isActivityInChooseBeautyTime() {
        if (activityInfo == null) {
            return false;
        }
        long now = System.currentTimeMillis() / 1000;
        return activityInfo.getBeginTime() <= now && now < activityInfo.getEndShowTime();
    }

    /**
     * 是否在领奖期前（展示期+活动期）
     */
    public static boolean isActivityBeforeRewardTime() {
        if (activityInfo == null) {
            return false;
        }
        long now = System.currentTimeMillis() / 1000;
        return activityInfo.getBeginShowTime() <= now && now < activityInfo.getEndTime();
    }

    /**
     * 是否在领奖时间内
     */
    public static boolean isActivityInRewardTime() {
        if (activityInfo == null) {
            return false;
        }
        return ActivityMgr.activityInRewardPeriod(activityInfo);
    }

    /**
     * 获取鼓的配置信息
     */
    public static List<PplDrumInfo> getDrumInfoList() {
        return drumInfoList;
    }

    /**
     * 泡泡龙活动道具
     */
    public static Map<Integer, PplGoods> getPplGoodsMap() {
        return pplGoodsMap;
    }

    /**
     * 随机取鼓下标
     */
    private static int randomDrumIndex(Random random) {
        int totalWeight = 0;
        for (PplDrumInfo drumInfo : drumInfoList) {
            totalWeight += drumInfo.getWeight();
        }
        int index = random.nextInt(totalWeight);
        for (int i = 0; i < drumInfoList.size(); i++) {
            index = index - drumInfoList.get(i).getWeight();
            if (index < 0) {
                return i;
            }
        }
        getLogger().error("PplMgr randomDrumIndex error!");
        return 0;
    }

    /**
     * 消除球
     */
    public static PplShootResult eliminateBalls(PplUserData userData, int x, int y, Random random) {
        PplShootResult result = new PplShootResult();
        List<BallEliminate> eliminateList = new ArrayList<>();
        List<PplLine> pplLineList = userData.getPplLineList();
        int startY = pplLineList.get(0).getY();
        // 游戏结束不能再发射球了
        if (isGameOver(userData, userData.getCurMultiType())) {
            result.setRet(GameErrorCode.E_ACTIVITY_PPL_HAD_GAME_OVER);
            return result;
        }
        // 放置球
        if (startY - y == 1) {
            buildDownEmptyLine(userData);
            startY = startY - 1;
        }
        if (!canStopOnXy(userData, x, y)) {
            result.setRet(GameErrorCode.E_ACTIVITY_PPL_POSITION_ERROR);
            return result;
        }
        pplLineList.get(y - startY).getBallArray()[x] = userData.getCurColor();
        // 统计同颜色数<Y,List<X>>
        Map<Integer, List<Integer>> sameColorMap = new HashMap<>();
        int sameColorCount = countSameColorBalls(pplLineList, x, y, sameColorMap);
        // 消除
        int eliminateMaxY = 0; // 记录消除的最高Y，用于处理掉落
        if (sameColorCount >= ELIMINATE_NEED_COUNT) {
            for (Map.Entry<Integer, List<Integer>> entry : sameColorMap.entrySet()) {
                int tmpY = entry.getKey();
                List<Integer> tmpXList = entry.getValue();
                int[] ballArray = pplLineList.get(tmpY - startY).getBallArray();
                for (Integer tmpX : tmpXList) {
                    int drumIndex = randomDrumIndex(random);
                    int count = userData.getDrumList().get(drumIndex);

                    if (userData.getCurMultiType() == 3) {
                        userData.getDrumList().set(drumIndex, count + 16);
                    } else if (userData.getCurMultiType() == 2) {
                        userData.getDrumList().set(drumIndex, count + 4);
                    } else {
                        userData.getDrumList().set(drumIndex, count + 1);
                    }

                    BallEliminate eliminate = new BallEliminate(tmpX, tmpY, ballArray[tmpX], drumIndex);
                    eliminateList.add(eliminate);
                    ballArray[tmpX] = 0;
                }
                eliminateMaxY = eliminateMaxY >= tmpY ? eliminateMaxY : tmpY;
            }
        } else {
            // 没有消除
            return result;
        }
        // 从最顶部开始判断掉落
        boolean borrowTopLine = false;
        if (eliminateMaxY == userData.getCurMaxY()) { // 如果消除到最上面一行，先加一行再判断掉落
            borrowTopLine = true;
            addOneLine(userData, random);
        }
        int[] ballArray = pplLineList.get(userData.getCurMaxY() - startY).getBallArray();
        Map<Integer, Set<Integer>> linkedMap = new HashMap<>(); // Map<Y坐标,Set<X坐标>>
        for (int tmpX = 0; tmpX < ballArray.length; tmpX++) {
            if (ballArray[tmpX] != 0) {
                countLinkedBalls(pplLineList, tmpX, userData.getCurMaxY(), linkedMap);
            }
        }
        // 未连接的掉落
        for (int i = 0; i < pplLineList.size(); i++) {
            PplLine tmpPplLine = pplLineList.get(i);
            int[] tmpBallArray = tmpPplLine.getBallArray();
            for (int tmpX = 0; tmpX < tmpBallArray.length; tmpX++) {
                if (tmpBallArray[tmpX] != 0) {
                    if (!(linkedMap.containsKey(tmpPplLine.getY()) && linkedMap.get(tmpPplLine.getY()).contains(tmpX))) {
                        int drumIndex = randomDrumIndex(random);
                        int count = userData.getDrumList().get(drumIndex);

                        if (userData.getCurMultiType() == 3) {
                            userData.getDrumList().set(drumIndex, count + 16);
                        } else if (userData.getCurMultiType() == 2) {
                            userData.getDrumList().set(drumIndex, count + 4);
                        } else {
                            userData.getDrumList().set(drumIndex, count + 1);
                        }

                        BallEliminate eliminate = new BallEliminate(tmpX, tmpPplLine.getY(), tmpBallArray[tmpX], drumIndex);
                        eliminateList.add(eliminate);
                        tmpBallArray[tmpX] = 0;
                    }
                }
            }
        }
        // 清除空行
        List<PplLine> toDeleteLine = new ArrayList<>();
        for (int i = 0; i < pplLineList.size(); i++) {
            if (isLineEmpty(pplLineList.get(i))) {
                toDeleteLine.add(pplLineList.get(i));
            }
        }
        pplLineList.removeAll(toDeleteLine);
        // 清除顶行的球，且不需要新增行时，则借的那行删掉（仅用来判断掉落，不判断消除）
        if (borrowTopLine && pplLineList.size() > SHOW_LENGTH) {
            pplLineList.remove(pplLineList.size() - 1);
            userData.setCurMaxY(pplLineList.get(pplLineList.size() - 1).getY()); // 独立设置新的Y
        }
        // 增加行
        autoAddLine(userData, random);

        result.setEliminateList(eliminateList);
        return result;
    }

    /**
     * 统计相邻点（包括自己）
     *
     * @param linkedBallMap 存放结果
     */
    private static void countLinkedBalls(List<PplLine> pplLineList, int x, int y,
                                         Map<Integer, Set<Integer>> linkedBallMap) {
        // 先把自己加进去
        if (!linkedBallMap.containsKey(y)) {
            linkedBallMap.put(y, new HashSet<>());
        }
        if (!linkedBallMap.get(y).add(x)) {
            return; // 统计过该点了
        }
        // 下方的4个点，根据下标对应
        int[] yArray = {y - 1, y - 1, y, y};
        int[] xArray;
        if (y % 2 == 0) {
            int[] tmp = {x, x + 1, x - 1, x + 1};
            xArray = tmp;
        } else {
            int[] tmp = {x - 1, x, x - 1, x + 1};
            xArray = tmp;
        }
        // 统计
        for (int i = 0; i < xArray.length; i++) {
            if (isXyExist(pplLineList, xArray[i], yArray[i]) && getXyColor(pplLineList, xArray[i], yArray[i]) != 0) {
                countLinkedBalls(pplLineList, xArray[i], yArray[i], linkedBallMap);
            }
        }
    }

    /**
     * 统计同颜色数（包括自己）
     *
     * @param sameColorMap 记录同颜色的坐标 Map<Y坐标,List<X坐标>>
     */
    private static int countSameColorBalls(List<PplLine> pplLineList, int x, int y,
                                           Map<Integer, List<Integer>> sameColorMap) {
        // 相邻的6个点，根据下标对应
        int[] yArray = {y - 1, y - 1, y, y, y + 1, y + 1};
        int[] xArray;
        if (y % 2 == 0) {
            int[] tmpArray = {x, x + 1, x - 1, x + 1, x, x + 1};
            xArray = tmpArray;
        } else {
            int[] tmpArray = {x - 1, x, x - 1, x + 1, x - 1, x};
            xArray = tmpArray;
        }
        // 把自己加进去
        int sum = 1;
        if (!sameColorMap.containsKey(y)) {
            sameColorMap.put(y, new ArrayList<>());
        }
        sameColorMap.get(y).add(x);
        // 统计
        int startY = pplLineList.get(0).getY();
        int color = pplLineList.get(y - startY).getBallArray()[x];
        for (int i = 0; i < xArray.length; i++) {
            if (isXyExist(pplLineList, xArray[i], yArray[i]) && getXyColor(pplLineList, xArray[i], yArray[i]) > 0) { // 坐标合法 && 有颜色
                if (pplLineList.get(yArray[i] - startY).getBallArray()[xArray[i]] == color) { // 颜色相同
                    if (!(sameColorMap.containsKey(yArray[i]) && sameColorMap.get(yArray[i]).contains(xArray[i]))) { // 未统计过该点
                        // 递归统计
                        sum += countSameColorBalls(pplLineList, xArray[i], yArray[i], sameColorMap);
                    }
                }
            }
        }
        return sum;
    }

    /**
     * 自动补充行数
     */
    public static void autoAddLine(PplUserData userData, Random random) {
        List<PplLine> pplLineList = userData.getPplLineList();
        int maxY = userData.getCurMaxY();
        int minY = pplLineList.size() > 0 ? pplLineList.get(0).getY() : maxY;
        int addLineCount = pplLineList.size() == 0 ? SHOW_LENGTH : SHOW_LENGTH - (maxY - minY) - 1;
        for (int i = 0; i < addLineCount; i++) {
            addOneLine(userData, random);
        }
    }

    /**
     * 自动添加新的鼓
     */
    public static void autoAddDrum(PplUserData userData) {
        List<Integer> drumList = userData.getDrumList();
        if (drumInfoList.size() > drumList.size()) {
            for (int i = drumList.size(); i < drumInfoList.size(); i++) {
                drumList.add(0);
            }
        }
    }

    /**
     * 下方新建一空行
     */
    private static void buildDownEmptyLine(PplUserData userData) {
        List<PplLine> pplLineList = userData.getPplLineList();
        int minY = pplLineList.size() > 0 ? pplLineList.get(0).getY() : userData.getCurMaxY() + 1;
        PplLine pplLine = new PplLine();
        pplLine.setBallArray(new int[MAX_BALLS_COUNT]);
        pplLine.setY(minY - 1);
        pplLineList.add(0, pplLine);
    }

    /**
     * 添加新的一行
     */
    private static void addOneLine(PplUserData userData, Random random) {
        PplLine pplLine;
        if (userData.getCurMaxY() < INIT_Y + 9 && userData.getScore() == 0) {
            // 前9行要写死
            pplLine = buildStartOneLine(userData.getCurMaxY() + 1);
        } else {
            pplLine = buildOneLine(userData.getCurMaxY() + 1, userData.getHeadColor(), random);
        }
        List<PplLine> pplLineList = userData.getPplLineList();
        pplLineList.add(pplLine);
        userData.setCurMaxY(pplLineList.get(pplLineList.size() - 1).getY());
        userData.setHeadColor(calHeadColor(pplLine));
    }

    /**
     * 获取新的一行
     */
    private static PplLine buildOneLine(int y, int headColor, Random random) {
        int[] balls = new int[MAX_BALLS_COUNT];
        int count = calBuildBallCount(y);
        int startY = calBuildStartX(y);
        int addNum = isBuildFromLeft(y) ? 1 : -1;
        int lastColor = headColor;
        int sameColorCount = 0;
        int ratioType = isShortLine(y) ? 2 : 1; // 概率公式类型
        // 首个颜色
        balls[startY] = calNextBallColor(lastColor, 3, 0, random);
        lastColor = balls[startY];
        startY += addNum;
        count--;
        // 后续颜色
        for (; count > 0; count--) {
            int tmpColor = calNextBallColor(lastColor, ratioType, sameColorCount, random);
            if (tmpColor == lastColor) {
                sameColorCount++;
            } else {
                sameColorCount = 0;
            }
            balls[startY] = tmpColor;
            lastColor = tmpColor;
            startY += addNum;
        }
        // 返回结果
        PplLine pplLine = new PplLine();
        pplLine.setY(y);
        pplLine.setBallArray(balls);
        return pplLine;
    }

    /**
     * 计算下一个颜色
     *
     * @param lastColor      上一个的颜色    为宝箱时，随机一个颜色
     * @param ratioType      概率公式        1：长行连续，2：短行连续，3：每行首个
     * @param sameColorCount 已有同颜色数    从0开始
     * @return int 颜色
     */
    private static int calNextBallColor(int lastColor, int ratioType, int sameColorCount, Random random) {
        // 如果前一个没颜色，则4个随机
        if (lastColor <= 0) {
            return random.nextInt(TOTAL_COLOR_COUNT) + 1;
        }
        // 先判断同色
        if (ratioType == 1 && random.nextInt(1000) < SAME_COLOR_RATIO_A - sameColorCount * SAME_COLOR_RATIO_C
                || ratioType == 2 && random.nextInt(1000) < SAME_COLOR_RATIO_B
                || ratioType == 3 && random.nextInt(1000) < SAME_COLOR_RATIO_A) {
            return lastColor;
        }
        // 再判断宝箱
        if (random.nextInt(1000) < BOX_RATIO) {
            return -1;
        }
        // 剩下的随机
        int tmpColor = random.nextInt(TOTAL_COLOR_COUNT - 1) + 1;
        return tmpColor == lastColor ? TOTAL_COLOR_COUNT : tmpColor;
    }

    /**
     * 生成几个
     */
    private static int calBuildBallCount(int y) {
        return y % 3 == 2 ? SHORT_BALLS_COUNT : LONG_BALLS_COUNT;
    }

    /**
     * 起始下标
     */
    private static int calBuildStartX(int y) {
        switch (y % 6) {
            case 0:
                return 8;
            case 1:
            case 3:
            case 4:
                return 1;
            case 2:
                return 0;
            case 5:
                return 9;
            default:
                getLogger().error("PplMgr calBuildStartX error, param y may below zero!");
                return 1;
        }
    }

    /**
     * 是否从左往右生成
     */
    private static boolean isBuildFromLeft(int y) {
        return (y + 5) % 6 <= 3;
    }

    /**
     * 是否是短的行
     */
    private static boolean isShortLine(int y) {
        return y % 3 == 2;
    }

    /**
     * 获取用于生成下一行的颜色参数
     */
    private static int calHeadColor(PplLine pplLine) {
        int[] ballArray = pplLine.getBallArray();
        if (isBuildFromLeft(pplLine.getY() + 1)) {
            // 下一行顺序生成，需要上一行最左边
            for (int i = 0; i < ballArray.length; i++) {
                if (ballArray[i] != 0) {
                    return ballArray[i];
                }
            }
        } else {
            // 下一行逆序生成，需要上一行最右边
            for (int i = ballArray.length - 1; i > 0; i--) {
                if (ballArray[i] != 0) {
                    return ballArray[i];
                }
            }
        }
        getLogger().error("PplMgr calHeadColor error!!!y={},ballArrayJson={}", pplLine.getY(), JSON.toJSONString(ballArray));
        return new Random().nextInt(TOTAL_COLOR_COUNT) + 1;
    }

    /**
     * 是否为空行
     */
    private static boolean isLineEmpty(PplLine pplLine) {
        int[] ballArray = pplLine.getBallArray();
        for (int i = 0; i < ballArray.length; i++) {
            if (ballArray[i] != 0) {
                return false;
            }
        }
        return true;
    }

    /**
     * 坐标是否存在
     */
    private static boolean isXyExist(List<PplLine> pplLineList, int x, int y) {
        if (pplLineList == null || pplLineList.size() == 0) {
            return false;
        }
        int startY = pplLineList.get(0).getY();
        int maxY = pplLineList.get(pplLineList.size() - 1).getY();
        if (y < startY || y > maxY) {
            return false;
        }
        PplLine pplLine = pplLineList.get(y - startY);
        return x >= 0 && x < pplLine.getBallArray().length;
    }

    /**
     * 获取颜色
     */
    private static int getXyColor(List<PplLine> pplLineList, int x, int y) {
        int startY = pplLineList.get(0).getY();
        int[] ballArray = pplLineList.get(y - startY).getBallArray();
        return ballArray[x];
    }

    /**
     * 是否游戏结束
     */
    public static boolean isGameOver(PplUserData userData, int multiType) {
        if (multiType == 1) {
            return userData.getPplLineList1().size() >= GAME_OVER_LENGTH;
        } else if (multiType == 2) {
            return userData.getPplLineList4().size() >= GAME_OVER_LENGTH;
        } else if (multiType == 3) {
            return userData.getPplLineList16().size() >= GAME_OVER_LENGTH;
        }
        getLogger().error("PplMgr isGameOver error! multiType = {}", multiType);
        return true;
    }

    /**
     * 能否停留在该位置
     */
    private static boolean canStopOnXy(PplUserData pplUserData, int x, int y) {
        List<PplLine> pplLineList = pplUserData.getPplLineList();
        // 判断：该点是否存在、是否已有值
        if (!isXyExist(pplLineList, x, y) || getXyColor(pplLineList, x, y) != 0) {
            return false;
        }
        // 判断：是否有相邻点
        int[] yArray = {y - 1, y - 1, y, y, y + 1, y + 1};
        int[] xArray;
        if (y % 2 == 0) {
            int[] tmpArray = {x, x + 1, x - 1, x + 1, x, x + 1};
            xArray = tmpArray;
        } else {
            int[] tmpArray = {x - 1, x, x - 1, x + 1, x - 1, x};
            xArray = tmpArray;
        }
        for (int i = 0; i < xArray.length; i++) {
            if (isXyExist(pplLineList, xArray[i], yArray[i]) && getXyColor(pplLineList, xArray[i], yArray[i]) != 0) {
                return true;
            }
        }
        return false;
    }

    /**
     * 加载用户数据
     */
    private static void reloadPlayerData() {
        if (isActivityInShowTime()) {
            int activityId = getShowTimeActivityId();
            for (GamePlayer player : GamePlayerMgr.getCopyPlayerList()) {
                if (SystemOpenMgr.systemOpen(player, eSystemId.Ppl.getValue())) {
                    PplModule pplModule = player.getModule(PplModule.class);
                    pplModule.dailyReset();
                    if (pplModule.getUserData() == null || pplModule.getUserData().getActivityId() != activityId) {
                        pplModule.initUserData();
                        pplModule.syncEnergy();
                    }
                }
            }
        }
    }

    /**
     * 活动道具添加到Map
     */
    private static void initPplGoodsMap() {
        GoodsInfo energy = CommonMgr.getGoodsInfo(PPL_ENERGY_RECOVERY_ID);
        GoodsInfo book1 = CommonMgr.getGoodsInfo(PPL_BOOK_MOUNTAIN_RIVER_ID);
        GoodsInfo book2 = CommonMgr.getGoodsInfo(PPL_BOOK_FISHING_SONG_ID);
        Map<Integer, PplGoods> tmp = new ConcurrentHashMap<>();
        tmp.put(PPL_ENERGY_RECOVERY_ID, PplGoods.newEnergy(PPL_ENERGY_RECOVERY_ID, energy.getParamList().get(0).intValue()));
        tmp.put(PPL_BOOK_MOUNTAIN_RIVER_ID, PplGoods.newBook(PPL_BOOK_MOUNTAIN_RIVER_ID, book1.getParamList().get(0).intValue(), book1.getExtendParam()));
        tmp.put(PPL_BOOK_FISHING_SONG_ID, PplGoods.newBook(PPL_BOOK_FISHING_SONG_ID, book2.getParamList().get(0).intValue(), book2.getExtendParam()));
        pplGoodsMap = tmp;
    }

    /**
     * 获取宝箱球的随机奖励
     */
    public static String getBoxRandomReward() {
        OpenGoodsBagResult openGoodsBagResult = GoodsBagMgr.getRandomGoods(PPL_BOX_BALL_BOX_ID);
        return PropertyHelper.parsePropertyToString(openGoodsBagResult.getReward());
    }

    /**
     * 创建新手行数
     */
    private static PplLine buildStartOneLine(int y) {
        PplLine pplLine = new PplLine();
        pplLine.setY(y);
        switch (y) {
            case INIT_Y + 1:
                pplLine.setBallArray(new int[]{0, -1, 3, 3, 3, 3, 3, 4, 3, 0});
                break;
            case INIT_Y + 2:
                pplLine.setBallArray(new int[]{0, 1, 2, 4, 3, 1, 2, 4, 3, 0});
                break;
            case INIT_Y + 3:
                pplLine.setBallArray(new int[]{4, 3, 0, 0, 0, 0, 0, 0, 0, 0});
                break;
            case INIT_Y + 4:
                pplLine.setBallArray(new int[]{0, 1, 2, 4, 3, 1, 2, 4, 3, 0});
                break;
            case INIT_Y + 5:
                pplLine.setBallArray(new int[]{0, 1, 2, 4, 3, 1, 2, 4, 3, 0});
                break;
            case INIT_Y + 6:
                pplLine.setBallArray(new int[]{0, 0, 0, 0, 0, 0, 0, 0, 4, 3});
                break;
            case INIT_Y + 7:
                pplLine.setBallArray(new int[]{0, 1, 2, 4, 3, 1, 2, 4, 3, 0});
                break;
            case INIT_Y + 8:
                pplLine.setBallArray(new int[]{0, 1, 2, 4, 3, 1, 2, 4, 3, 0});
                break;
            case INIT_Y + 9:
                pplLine.setBallArray(new int[]{4, 3, 0, 0, 0, 0, 0, 0, 0, 0});
                break;
        }
        return pplLine;
    }
}
